package com.dc.base.aop;

import com.dc.base.contants.GlobalVar;
import com.dc.base.em.ErrorMessageEnum;
import com.dc.base.em.RoleMenuEnum;
import com.dc.base.vo.BaseModel;
import com.dc.base.vo.BusinessException;
import com.dc.dao.OperationLogDao;
import com.dc.model.Menu;
import com.dc.model.OperationLog;
import com.dc.model.User;
import com.dc.service.MenuService;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Date;
import java.util.Map;


@Aspect
@Component
public class OperationAspect {
    protected HttpSession session;
    protected HttpServletRequest request;
    protected BaseModel baseModel=new BaseModel();
    protected Logger log=Logger.getLogger(OperationAspect.class);
    @Autowired
    OperationLogDao operationLogDao;
    @Autowired
    MenuService menuService;
    @Pointcut("execution(* com.dc.controller..*.*(..))")
    public void pointCut(){

    }
    //实现通知，前置通知：执行业务方法前执行
    // 环绕通知： 既能在方法前执行，又能再方法后执行
    @Before("pointCut()&&@annotation(operation)")
    public void doBefore(JoinPoint joinPoint,AopOperation operation) throws Exception {
        // 前置通知，获取到切点方法内的参数，判断用户是否登录
        request=((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
        session=request.getSession();
        //获取baseModel
       Object[] params= joinPoint.getArgs();
       for(int i=0;i<params.length;i++){
            if(params[i]!=null&&params[i].getClass() == BaseModel.class){
                baseModel=(BaseModel)params[i];
            }
       }
       User user=(User)session.getAttribute("currentUser");
       if(user==null){
           throw new BusinessException(ErrorMessageEnum.LOGIN_TIMEOUT);
       }
       // 定义权限拦截
        checkMenu(operation);

    }

    public void checkMenu(AopOperation operation)throws Exception{
        //获取菜单编号，确定操作的模块是什么
        //判断是否拥有权限
        RoleMenuEnum module = operation.menu();//获取操作模块
        if (operation.interceptLogin()) {
            //取出登录用户的所有权限
            Map<String, String> mapPermission = (Map<String, String>) session.getAttribute("permission");
            if (mapPermission == null) {//说明未登录
                throw new BusinessException(ErrorMessageEnum.LOGIN_TIMEOUT);
            } else {
                String permission = mapPermission.get(module.getCode() + "");
                //如果全部菜单的map为空，那么调用查询全部菜单的业务层，使静态类中获取数据
                if (GlobalVar.mapCodeMenu == null) {
                    menuService.selectAllMenu();
                }
                //是否需要进行权限拦截
                if (operation.interceptPermission()) {
                    if (permission == null) {//如果权限值为空，则说明无权限
                        throw new BusinessException(ErrorMessageEnum.NO_PERMISSIONS);
                    }
                    String[] valueNames = module.getRoleValue().split(",");
                    for (int i = 0; i < valueNames.length && permission.length() > i; i++) {
                        if (valueNames[i].equals(operation.type())) {
                            //判断操作的类型所在序列
                            if (permission.charAt(i) != '1') {
                                //说明无权限
                                throw new BusinessException(ErrorMessageEnum.NO_PERMISSIONS);
                            }
                        }
                    }
                }
                baseModel.setPermission(permission);//返回操作的模块的权限值给baseModel
            }
        }
    }


    // 后置通知：业务方法后执行
    @After("pointCut()&&@annotation(operation)")
    public void doAfter(JoinPoint joinPoint,AopOperation operation){
        // 首先判断一下是否需要进行操作日志的新增
        if(operation.saveLog()){
            User user = (User) session.getAttribute("currentUser");
            if (user == null) {
                throw new BusinessException(ErrorMessageEnum.LOGIN_TIMEOUT);
            }
            //需要记录操作日志
            OperationLog operationLog=new OperationLog();
            operationLog.setUser_id(user.getUser_id());
            operationLog.setUser_name(user.getName());
            operationLog.setUser_account(user.getAccount());
            // ip地址
            operationLog.setRequest_ip(request.getRemoteAddr());
            //操作模块
            operationLog.setModule(operation.menu().getDescribe());
            //操作类型
            operationLog.setType(operation.type());
            operationLog.setContent(operation.desc());
            operationLog.setOperation_time(new Date());
            operationLog.setRequest_method(joinPoint.getTarget().getClass().getName()
                    +"."+joinPoint.getSignature());
            //新增操作日志
            int result=operationLogDao.insertOperationLog(operationLog);
            if(result<=0){
                throw new BusinessException(1010,"新增操作日志失败！");
            }
        }
    }


}
